// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <pollux/expression/function_signature.h>
#include <pollux/expression/vector_function.h>
#include <pollux/functions/macros.h>
#include <pollux/functions/prestosql/types/ip_prefix_type.h>

namespace kumo::pollux {

template <typename T>
struct FuncOne {
  POLLUX_DEFINE_FUNCTION_TYPES(T);

  // Set func_one as non-deterministic.
  static constexpr bool is_deterministic = false;

  MELON_ALWAYS_INLINE bool call(
      out_type<pollux::Varchar>& /* result */,
      const arg_type<pollux::Varchar>& /* arg1 */) {
    return true;
  }
};

template <typename T>
struct FuncTwo {
  template <typename T1, typename T2>
  MELON_ALWAYS_INLINE bool callNullable(
      int64_t& /* result */,
      const T1* /* arg1 */,
      const T2* /* arg2 */) {
    return true;
  }
};

template <typename T>
struct FuncThree {
  POLLUX_DEFINE_FUNCTION_TYPES(T);

  MELON_ALWAYS_INLINE bool call(
      ArrayWriter<int64_t>& /* result */,
      const ArrayVal<int64_t>& /* arg1 */) {
    return true;
  }
};

template <typename T>
struct FuncFour {
  POLLUX_DEFINE_FUNCTION_TYPES(T);

  MELON_ALWAYS_INLINE bool call(
      out_type<pollux::Varchar>& /* result */,
      const arg_type<pollux::Varchar>& /* arg1 */) {
    return true;
  }
};

template <typename T>
struct FuncFive {
  MELON_ALWAYS_INLINE bool call(int64_t& result, const int64_t& /* arg1 */) {
    result = 5;
    return true;
  }
};

// FuncSix has the same signature as FuncFive. It's used to test overwrite
// during registration.
template <typename T>
struct FuncSix {
  MELON_ALWAYS_INLINE bool call(int64_t& result, const int64_t& /* arg1 */) {
    result = 6;
    return true;
  }
};

template <typename T>
struct VariadicFunc {
  POLLUX_DEFINE_FUNCTION_TYPES(T);

  MELON_ALWAYS_INLINE bool call(
      out_type<pollux::Varchar>& /* result */,
      const arg_type<Variadic<pollux::Varchar>>& /* arg1 */) {
    return true;
  }
};

template <typename T>
struct IPPrefixFunc {
  POLLUX_DEFINE_FUNCTION_TYPES(T);

  MELON_ALWAYS_INLINE bool call(
      out_type<IPPrefix>& /* result */,
      const arg_type<IPPrefix>& /* arg1 */) {
    return true;
  }
};

class VectorFuncOne : public pollux::exec::VectorFunction {
 public:
  void apply(
      const pollux::SelectivityVector& /* rows */,
      std::vector<pollux::VectorPtr>& /* args */,
      const TypePtr& /* outputType */,
      pollux::exec::EvalCtx& /* context */,
      pollux::VectorPtr& /* result */) const override {}

  static std::vector<std::shared_ptr<pollux::exec::FunctionSignature>>
  signatures() {
    // varchar -> bigint
    return {pollux::exec::FunctionSignatureBuilder()
                .returnType("bigint")
                .argumentType("varchar")
                .build()};
  }
};

class VectorFuncTwo : public pollux::exec::VectorFunction {
 public:
  void apply(
      const pollux::SelectivityVector& /* rows */,
      std::vector<pollux::VectorPtr>& /* args */,
      const TypePtr& /* outputType */,
      pollux::exec::EvalCtx& /* context */,
      pollux::VectorPtr& /* result */) const override {}

  static std::vector<std::shared_ptr<pollux::exec::FunctionSignature>>
  signatures() {
    // array(varchar) -> array(bigint)
    return {pollux::exec::FunctionSignatureBuilder()
                .returnType("array(bigint)")
                .argumentType("array(varchar)")
                .build()};
  }
};

class VectorFuncThree : public pollux::exec::VectorFunction {
 public:
  void apply(
      const pollux::SelectivityVector& /* rows */,
      std::vector<pollux::VectorPtr>& /* args */,
      const TypePtr& /* outputType */,
      pollux::exec::EvalCtx& /* context */,
      pollux::VectorPtr& /* result */) const override {}

  static std::vector<std::shared_ptr<pollux::exec::FunctionSignature>>
  signatures() {
    // ... -> opaque
    return {pollux::exec::FunctionSignatureBuilder()
                .returnType("opaque")
                .argumentType("any")
                .build()};
  }
};

class VectorFuncFour : public pollux::exec::VectorFunction {
 public:
  void apply(
      const pollux::SelectivityVector& /* rows */,
      std::vector<pollux::VectorPtr>& /* args */,
      const TypePtr& /* outputType */,
      pollux::exec::EvalCtx& /* context */,
      pollux::VectorPtr& /* result */) const override {}

  static std::vector<std::shared_ptr<pollux::exec::FunctionSignature>>
  signatures() {
    // map(K,V) -> array(K)
    return {pollux::exec::FunctionSignatureBuilder()
                .knownTypeVariable("K")
                .typeVariable("V")
                .returnType("array(K)")
                .argumentType("map(K,V)")
                .build()};
  }
};

} // namespace kumo::pollux
